prototype
- Object.prototype
- 是
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
- 是
- Function.prototype
- 是
ƒ () { [native code] }
- 是
- Foo.prototype (宣告
function Foo(){}
後)- 是
{constructor: ƒ}
- 是
__proto__
所有 instance 的 __proto__
是 其 internal prototype
例如:
new Object().__proto__
- 是 Object.prototype
(function Foo(){}).__proto__
- 是 Function.prototype
new Foo().__proto__
- 是 Foo.prototype
prototype 的 __proto__
除了 Object prototype 的 __proto__
是 null
,
其他都是 Object.prototype
Object.prototype.__proto__
- 是 null
Function.prototype.__proto__
- 是 Object.prototype
Foo.prototype.__proto__
- 是 Object.prototype
Function 的 __proto__
Function 的 __proto__
都是 Function.prototype
Object.__proto__
- 是 Function.prototype
Function.__proto__
- 是 Function.prototype
Foo.__proto__
- 是 Function.prototype
constructor
constructor
與 prototype
互為 reversion
JS
https://marketplace.visualstudio.com/items?itemName=Wscats.eno
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>External JavaScript example</title>
<style></style>
<link rel="stylesheet" href="css/mystyle.css" />
<script src="js/script.js" defer></script>
</head>
<body>
<button>Click me</button>
<script></script>
</body>
</html>
script defer async
<script src="script.js" defer></script>
在這個例子中,腳本(JavaScript 程式)與 HTML 會同時載入,所以程式將正確地執行。 在外部程式的範例裡,我們不需要使用 DOMContentLoaded 事件因為 defer 為我們解決問題了。而在內部程式的範例裡我們沒用 defer 屬性,是因為 defer 屬性只能用於外部的腳本。 這個問題有另一個舊式的解決方法,就是將 script 元素放在 body 元素的底部(剛好在</body>
的前面),如此它就會在所有 HTML 被解析完之後才被載入。這個方法的問題在於腳本的載入與解析工作會被完成擋住,一直到所有 HTML 載入完成。在擁有許多 JavaScript 的大型網站中,這樣會導致嚴重的效能問題,拖慢你的網站。 實際上,有兩個方法可以閃過腳本被擋到的問題:async 與 defer(前面看到的)。來看看兩者的區別。 async 和 defer 屬性都是用來告訴瀏覽器使用獨立線程(行程)來下載腳本,同一時間頁面的其它部分(如 DOM 元件之類)也在下載,因此頁面的載入不會因為腳本被影響。
- 如果你的腳本應該立即被執行,而且不依賴其它腳本先被載入,就用 async 。
- 如果你的腳本依賴其他腳本先被解析或 DOM 已經存在,就用 defer 來載入它們,並根據你想要瀏覽器執行的順序安排
<script>
元素的次序。 - 有 async 或 defer 的 js 會與 html 同步下載
- html -> async -> defer
Operators
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Expressions_and_Operators
a||b
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR if bool(a) is falsy, return b
a??b
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
if bool(a) is null or undefined
, return b
a?b:c
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Conditional_Operator if bool(a) is truthy, return b, else return c
a.b
when a is null or undefined
, throw error, else return a.b
a?.b
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Optional_chaining
when a is null or undefined
, return undefined, else return a.b
數
null 是一個表示“空”的對象,轉為數值時為 0;undefined 是一個表示"此處無定義"的原始值,轉為數值時為 NaN NaN null undefined Infinity
js 底層可是 253 ~= 1016, 所以 10**15 以內為精確 Number.MAX_VALUE // 1.7976931348623157e+308 Number.MIN_VALUE // 5e-324
不在 21024~2-1023 內會溢位
轉為 int32
function toInt32(x) {
return x | 0;
}
數字與字串轉換
let myNum = Number(myString);
let myString = myNum.toString();
手動轉換 type:
- Number() or parseInt()
- String()
- Boolean()
小數取整數
return ~~f;
return f ^ 0;
bitwise operator SHIFT RIGHT
>>
>>>
頭部補零的右移運算符(>>>)與右移運算符(>>)只有一個差別,就是一個數的二進制形式向右移動時,頭部一律補零,而不考慮符號位。
設計開關控制
const FLAGS_0 = 1 << 0;
const FLAGS_1 = 1 << 1;
const FLAGS_2 = 1 << 2;
const FLAGS_3 = 1 << 3;
const FLAGS = {
開關0: FLAGS_0,
開關1: FLAGS_1,
開關2: FLAGS_2,
開關3: FLAGS_3,
};
var flag = FLAGS_1 | FLAGS_3; // 設定開關 1&3 打開
Object.keys(FLAGS).forEach((key) => {
if (flag & FLAGS[key]) {
console.log(`${key} is on. `);
}
});
// 開關1 is on.
// 開關3 is on.
右結合的運算
- =
- ?:
- **
舉例
// 以下相同
w = x = y = z;
w = x = y = z;
q = a ? b : c ? d : e ? f : g;
q = a ? b : c ? d : e ? f : g;
2 ** (3 ** 2);
2 ** (3 ** 2);
兩整數交換
a ^= b, b ^= a, a ^= b;
let dog = {
name: "Spot",
breed: "Dalmatian",
};
console.log(dog.name); // Spot
字串
字串模板 Template literals
用 backtick characters (``)
常用 methods
string.length
string.indexOf('find') // 會回傳'find'首次出現在string的哪個index,沒找到會return -1
string.toLowerCase();
string.toUpperCase();
string.replace('moz','van');
string.repeat(2);
.includes()
str.slice(beginIndex[, endIndex])
.slice 可以把類似 Array 的object 轉成 Array
const str = "The quick brown fox jumps over the lazy dog.";
console.log(str.slice(31));
// expected output: "the lazy dog."
console.log(str.slice(4, 19));
// expected output: "quick brown fox"
console.log(str.slice(-4));
// expected output: "dog."
console.log(str.slice(-9, -5));
// expected output: "lazy"
多行字串
(function () {
/*
line 1
line 2
line 3
*/
}
.toString()
.split("\n")
.slice(1, -1)
.join("\n"));
反轉字串
revstr = Array.from(string).reverse().join("");
revstr = string.split("").reverse().join("");
array
string 轉換成 array
arr = Array.from(string);
arr.reverse(); // array 反轉
array 換 string
const elements = ["Fire", "Air", "Water"];
console.log(elements.join());
// expected output: "Fire,Air,Water"
console.log(elements.join(""));
// expected output: "FireAirWater"
console.log(elements.join("-"));
// expected output: "Fire-Air-Water"
array 是 object
var arr = [1, 2, 3];
console.log(typeof arr, arr, Object.keys(arr));
// object [ 1, 2, 3 ] [ '0', '1', '2' ]
arr["k"] = "v";
console.log(typeof arr, arr, Object.keys(arr));
// object [ 1, 2, 3, k: 'v' ] [ '0', '1', '2', 'k' ]
array 基本操作
var tmp;
var arr = ["0"];
console.log(arr);
tmp = arr.push("123"); // 加入最右
console.log(arr); // ["0", "123"]
console.log(tmp); // 2 (length)
tmp = arr.unshift("456", "789"); // 加入最左
console.log(arr); // ["456", "789", "0", "123"]
console.log(tmp); // 4 (length)
tmp = arr.pop(); // 移除最右
console.log(arr); // ["456", "789", "0"]
console.log(tmp); // 123
tmp = arr.shift(); // 移除最左
console.log(arr); // ["789", "0"]
console.log(tmp); // 456
將類 array 轉為 array
var a = [10, 20, 30];
console.log(a instanceof Array, a);
// true [ 10, 20, 30 ]
var arraylike = (function () {
return arguments;
})(10, 20, 30);
console.log(arraylike instanceof Array, arraylike);
// false [Arguments] { '0': 10, '1': 20, '2': 30 }
var arr;
arr = Array.prototype.slice.call(arraylike);
console.log(arr instanceof Array, arr);
// true [ 10, 20, 30 ]
搭配 for
var a = [10, 20, 30];
a.hi = "hi";
(() => {
let re = "";
for (let key in a) {
// 所有index
re += "(" + key + ", " + a[key] + "), ";
}
console.log(re);
})(); //(0, 10), (1, 20), (2, 30), (hi, hi),
(() => {
let re = "";
for (let i = 0; i < a.length; i++) {
//只有數字index
re += "(" + i + ", " + a[i] + "), ";
}
console.log(re);
})(); //(0, 10), (1, 20), (2, 30),
(() => {
let re = "";
a.forEach((e) => {
re += "(" + "" + ", " + e + "), ";
});
console.log(re);
})(); //(, 10), (, 20), (, 30),
foreach
Array.prototype.forEach.call(arr, func) 中的 func 最多可以傳入三個 arg,依序為:
- 值
- index
- 完整原傳入物件
function show_args(v, index, all) {
console.log(index + " : " + v, all);
}
var arrayLike = (function () {
return arguments;
})(10, 20, 30);
Array.prototype.forEach.call(arrayLike, show_args);
// 0 : 10 Arguments(3) [10, 20, 30, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 1 : 20 Arguments(3) [10, 20, 30, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 2 : 30 Arguments(3) [10, 20, 30, callee: ƒ, Symbol(Symbol.iterator): ƒ]
var o = [1, 2, 3];
Array.prototype.forEach.call(o, show_args);
// 0 : 1 (3) [1, 2, 3]
// 1 : 2 (3) [1, 2, 3]
// 2 : 3 (3) [1, 2, 3]
Object.entries()
const object1 = {
a: "somestring",
b: 42,
};
for (const [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}
document.querySelector & document.querySelectorAll
選取方式
- tag
- .class
- #id
處理第一筆
h2 的 tag
document.querySelector("h2").textContent = "123s";
處理全部
記下所有的 class="title"
var es = document.querySelectorAll(".title");
然後處理特定 index
es[0].textContent = "123";
es[1].textContent = "456a";
es[2].textContent = "789a";
或是放進迴圈一起處理
es.forEach((e) => {
e.textContent = "333";
});
屬性 attr
取得 attr
var get = el.getAttribute("href");
改變 attr
把屬性 href 改為 xx
el.setAttribute("href", "xx");
function
toString() 原始碼
function f(a, b) {}
console.log(f.toString()); //function f(a, b) {}
name()
var v02 = function f02(txt) {
console.log(txt);
};
console.log(v02.name); //f02
arguments[]
var f = function () {
console.log(arguments[0]);
console.log(arguments[1]);
};
f(0, 1, 2, 3);
//0
//1
var f = function (a, b) {
"use strict"; // 嚴格模式
arguments[0] = 3;
arguments[1] = 2;
return [a, b, arguments[0], arguments[1]];
};
console.log(f(1, 1));
//普通模式: [3, 2, 3, 2]
//嚴格模式: [1, 1, 3, 2]
var f = function f() {
let re = "";
for (let arg in arguments) {
re += arg + ", ";
}
console.log(re);
};
f(0, 1, 2, 3); //0, 1, 2, 3,
arguments 是 object , 可轉換成 array
var v = (function f() {
//1
var args1 = Array.prototype.slice.call(arguments);
//2
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
//output
console.log(args1, args2);
})(1, 2, 3); // [1,2,3] [1,2,3]
參數
length()
傳入的參數個數
function f(a, b) {}
f.length; // 2
參數傳遞方式
- 參數是數值、字符串、布爾值: pass by value
- 數組、對象、其他函數: pass by reference
修改參數 object 的屬性會影響, 因為 object 是 pass by reference
var obj = {
p: 1,
};
function f(o) {
o.p = 2;
return "->";
}
console.log(obj.p, f(obj), obj.p); //1 "->" 2
但是替換參數
不影響
var obj = {
p: 1,
};
function f(o) {
o = {
p: "qwertyusdgdgh",
};
return "->";
}
console.log(obj, f(obj), obj); //{p: 1} "->" {p: 1}
參數傳遞可不按照宣告數量
function f(a, b) {
if (b) {
return b;
} else if (a) {
return a;
} else {
return 0;
}
}
console.log(f.length); // 2
console.log(f("a", "b", "c")); // b
console.log(f("d", "e")); // e
console.log(f("f")); // f
console.log(f()); // 0
函數不同寫法
- function 可以提升
- var
- IIFE
f01("1. declaration 可以在宣告前呼叫");
function f01(txt) {
console.log(txt);
}
var v02 = function f02(txt) {
console.log(txt);
};
v02("2. expression 可省略func name");
(function f01(txt) {
console.log(txt);
})("3. 宣告時順便就呼叫");
若連續兩句法三的寫法中間要用分號隔開
使用 IIFE
// 不會污染 global
(function () {
var tmp = "hello";
console.log(tmp);
})();
// 會污染 global
var tmp = "hello";
console.log(tmp);
function f1(txt) {
console.log(txt);
}
f1("1. statement 另外呼叫 且必須有 func name");
var v2 = function f2(txt) {
console.log(txt);
};
v2("2. expression 另外呼叫");
var v3 = (function f3(txt) {
console.log(txt);
})("3. IIFE 直接呼叫");
var f4 = function f4(txt) {
return txt;
};
console.log(f4("4. expression 回傳值"));
var v5 = (function f5(txt) {
return txt;
})("5. IIFE 回傳值");
console.log(v5);
(function f6(txt) {
console.log(txt);
})("6. expression");
箭頭函式
https://developer.cdn.mozilla.net/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions
var a1 = (txt) => {
console.log(txt);
};
a1("1. 箭頭 另外呼叫");
var a2 = (txt) => {
return txt;
};
console.log(a2("2. 箭頭 回傳值"));
(參數1, 參數2, …, 參數N) => { 陳述式; }
(參數1, 參數2, …, 參數N) => 表示式;
// 等相同(參數1, 參數2, …, 參數N) => { return 表示式; }
// 只有一個參數時,括號才能不加:
(單一參數) => { 陳述式; }
單一參數 => { 陳述式; }
//若無參數,就一定要加括號:
() => { statements }
// 用大括號將內容括起來,返回一個物件字面值表示法:
params => ({foo: bar})
// 支援其餘參數與預設參數
(param1, param2, ...rest) => { statements }
(param1 = defaultValue1, param2, …, paramN = defaultValueN) => {
statements }
// 也支援 parameter list 的解構
var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
OO
function Person(name) {
this.name = name;
this.greeting = function () {
console.log("Hi! I'm " + this.name + ".");
};
}
var person1 = new Person("Bob");
var person2 = new Person("Sarah");
person1.greeting();
console.log(person1.name);
// Hi! I'm Bob.
// Bob
person2.greeting();
// Hi! I'm Sarah.
閉包
- 可以把函數內部變數往外傳遞
- 可以封裝私有 method
var v1 = function f1() {
var n = 999;
//閉包
return () => {
return n;
};
};
console.log(v1()()); //999
function plus_one(start) {
return () => {
return start++;
};
}
var inc = plus_one(5);
console.log(inc());
console.log(inc());
var inc = plus_one(5);
console.log(inc());
console.log(inc());
console.log(inc());
class 使用方式
class C {
constructor(name, age) {
this.name = name;
this.age = age;
}
hi() {
console.log(`hi im ${this.name}, ${this.age} years old.`);
}
}
var c = new C("cccc", 20);
c.hi();
function F(name, age) {
this.name = name;
this.age = age;
this.hi = function () {
console.log(`hi im ${this.name}, ${this.age} years old.`);
};
}
var f = new F("fffff", 20);
f.hi();
constructor
隨著 class 一同建立並初始化物件的特殊方法,一個 class 只能有一個稱為 constructor ,出現兩次以上就會發生 SyntaxError 錯誤,如果不指定建構子,就會使用預設的建構子。
預設的建構子長得像這樣:
constructor() {}
錯誤處理
function do2(txt) {
console.log(txt);
throw new Error("自訂錯誤訊息");
}
function do_something() {
try {
do2("1. try block內 -> error也會繼續執行");
} catch (e) {
console.log("2. try block 有error -> 才執行");
console.log("-> 印出error的追蹤");
console.log(e.stack);
} finally {
console.log("*此處一定會執行");
}
do2("3. try block外 -> 若error會停止程式");
console.log("4. 所以這裏執行不到");
}
do_something();
// 1. try block內 -> error也會繼續執行
// 2. try block 有error -> 才執行
// -> 印出error的追蹤
// Error: 自訂錯誤訊息
// at do2 (script.js:3)
// at do_something (script.js:8)
// at script.js:18
// *此處一定會執行
// 3. try block外 -> 若error會停止程式
// Uncaught Error: 自訂錯誤訊息
// at do2 (script.js:3)
// at do_something (script.js:14)
// at script.js:18
function f() {
try {
console.log(0);
throw 'bug';
} catch (e) {
console.log(1);
return 2; // 这句原本会延迟到 finally 代码块结束再执行
} finally {
console.log(3);
return 4; // 这句会覆盖掉 return 2
}
}
var result = f();
console.log(result)
// 若註解 return 4-> 0 1 3 2
// 若有 return 4-> 0 1 3 4
其他
顯示網頁上所有元素外框
[].forEach.call($$("*"),function(a){a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)})
1 == "1"; // true
1 === "1"; // false
expression 表達數值 statement 執行動作 object 物件
// 大括內先當作 statement or expression , 不能執行才被當作 object
{
1 + 2;
} // expression: 3
{
console.log(123);
} // statement: 123
{
foo: 123;
} // object: { foo: 123 }
// 多加一層小括會被當作 object
({ foo: 123 }); // object: {foo: 123}
// ({console.log(123)}) // SyntaxError
var o = {
6: 666,
hi: "hello",
};
console.log(o[3 + 3]); //666
console.log(o["h" + "i"]); //hello
console.log(o["6"]); //666
console.log(Object.keys(o)); //["6", "hi"]
console.log(Object.getOwnPropertyNames(o)); //["6", "hi"]
for (let k in o) {
console.log(`${k}: ${o[k]}`);
}
// 6: 666
// hi: hello
console.log("toString" in o); //true
console.log(o.hasOwnProperty("toString")); //false, 是ienumerable & 是繼承來的
console.log(o.toString); //ƒ toString() { [native code] } 或 [Function: toString]
console.log(o.toString()); //[object Object]
let word = "abc";
for (let i in word) {
console.log(i, word[i]);
}
// 0 a
// 1 b
// 2 c
可以在函數內部 call 自己
var pri = (function x() {
console.log(typeof x); //可以在函數內部call 自己
})(); //function
函數提升
var f = function () {
console.log("1");
};
function f() {
console.log("2");
}
f(); // 1
上面例子中, 表面上後面聲明的函數 f, 應該覆蓋前面的 var 賦值語句, 但是由於存在函數提升, 實際上正好反過來。
利用 void()使頁面不轉跳但是執行 func
<a href="javascript: void(document.form.submit())">按鈕</a>
自訂 type 顯示
var type = function (o) {
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1]; //.toLowerCase();
};
var types = [
type({}),
type([]),
type(5),
type(null),
type(),
type(/abcd/),
type(new Date()),
];
types.forEach((e) => {
console.log(e);
});
var v = 23;
console.log(typeof v);
// number
var v2 = new Number(23);
console.log(typeof v2);
// object
字串過濾
replace(/\D/g, "");
replace(/\W/g, "");
看 meta
document.querySelectorAll("meta").forEach((e) => {
console.log(e);
});
document.querySelectorAll("meta").forEach((e) => {
console.log(e.content);
});
get local storage by name
JSON.parse(decodeURIComponent(localStorage.getItem("name")));
get cookie value by name
JSON.parse(
decodeURIComponent(
((cookiename) => {
return document.cookie
.match(cookiename + "=[^;]+")[0]
.replace(/^[^=]+=/, "");
})("cart")
)
)[0]["commodity_id"];
cookie to key-val pair object
document.cookie.split("; ").reduce((accum, x) => {
const kv = x.split("=");
return { ...accum, ...{ [kv[0]]: kv[1] } };
}, {});
JSON.parse(
decodeURIComponent(
document.cookie.split("; ").reduce((accum, x) => {
const kv = x.split("=");
return { ...accum, ...{ [kv[0]]: kv[1] } };
}, {})["cart"]
)
)[0]["commodity_id"];
取自己的文字但是不包含子元素的文字
document.querySelector("span.price").childNodes[0].textContent;
$("span.price").clone().children().remove().end().text();
- document.querySelector().childNodes // 只 tag
- document.querySelector().children // tag&所有可放 text 的地方
小括弧功能
只會存最後一個,但會執行每個
var s = 5;
var t = (1, 2, 3, 4, (s += 10), 6, 7);
console.log(s, t); //15 7
參考資料
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Classes/constructor
https://hackmd.io/9UsfHCB2Rw2mAKTkXm6dWw#Number
https://ithelp.ithome.com.tw/articles/10228789
https://wangdoc.com/javascript/features/style.html
https://openhome.cc/Gossip/JavaGossip-V1/RegularExpression.htm